﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	EntityFactory.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;

namespace Smart.Common.Entities
{
	/// <summary>
	/// Entity factory
	/// </summary>
	public static class EntityFactory
	{
		public static SyncDictionary<string, EntityInfo> EntityNameMapping = new SyncDictionary<string, EntityInfo>(StringComparer.OrdinalIgnoreCase);
		public static SyncDictionary<Type, EntityInfo> EntityTypeMapping = new SyncDictionary<Type, EntityInfo>();
		public static SyncDictionary<string, EntityInfo> EntityContentTypeMapping = new SyncDictionary<string, EntityInfo>(StringComparer.OrdinalIgnoreCase);

		/// <summary>
		/// Entity factory
		/// </summary>
		static EntityFactory()
		{
		}

		public static bool Register<TEntityType>() where TEntityType : Entity
		{
			return Register(typeof(TEntityType));
		}

		public static bool Register(Type entityType)
		{
			if (entityType == null)
				throw new ArgumentNullException();

			if (!entityType.IsSubclassOf(typeof(Entity)) && entityType != typeof(Entity))
				return false;

			var ei = new EntityInfo(entityType);

			EntityNameMapping[ei.Name] = ei;
			EntityTypeMapping[ei.Type] = ei;

			if (!ei.ContentType.IsNullOrEmpty())
				EntityContentTypeMapping[ei.ContentType] = ei;

			return true;
		}

		public static EntityInfo QueryEntity(string entityName)
		{
			return EntityNameMapping.SafeGet(entityName);
		}

		public static EntityInfo QueryEntity(Type type)
		{
			return EntityTypeMapping.SafeGet(type);
		}

		public static EntityInfo QueryEntityByContentType(string contentType)
		{
			return EntityContentTypeMapping.SafeGet(contentType);
		}

		public static bool Unregister(string entityName)
		{
			if (entityName.IsNullOrEmpty())
				return false;

			var ei = QueryEntity(entityName);

			if (ei == null)
				return true;

			EntityNameMapping.Remove(ei.Name);
			EntityTypeMapping.Remove(ei.Type);

			if (!ei.ContentType.IsNullOrEmpty())
				EntityContentTypeMapping.Remove(ei.ContentType);

			return true;
		}

		public static bool Unregister(Type entityType)
		{
			if (entityType == null)
				throw new ArgumentNullException();

			var ei = QueryEntity(entityType);

			if (ei == null)
				return true;

			EntityNameMapping.Remove(ei.Name);
			EntityTypeMapping.Remove(ei.Type);

			if (!ei.ContentType.IsNullOrEmpty())
				EntityContentTypeMapping.Remove(ei.ContentType);

			return true;
		}

#if USE_CACHE_LASTENTITY
		[ThreadStatic] 
		private static EntityInfo _lastEntityInfo;
#endif

		public static Entity CreateEntity(string entityName, IEntityList entityList, IEntityMapper mapper)
		{
			if (entityName.IsNullOrEmpty())
				throw new ArgumentNullException();

			EntityInfo entityInfo = null;

#if USE_CACHE_LASTENTITY
			if (_lastEntityInfo != null && _lastEntityInfo.Name.EqIC(entityName))
				entityInfo = _lastEntityInfo;

			if (entityInfo == null)
				entityInfo = EntityNameMapping.SafeGet(entityName);
#else
			entityInfo = EntityNameMapping.SafeGet(entityName);
#endif

			if (entityInfo == null)
				throw new ArgumentOutOfRangeException();

#if USE_CACHE_LASTENTITY
			_lastEntityInfo = entityInfo;
#endif

			var entity =
				mapper != null
					? mapper.CreateEntity(entityInfo.Type)
					: (Entity)Activator.CreateInstance(entityInfo.Type);

			entity.List = entityList;

			entity.OnCreated();

			return entity;
		}

		public static Entity CreateEntity(Type entityType, IEntityList entityList, IEntityMapper mapper)
		{
			if (entityType == null)
				throw new ArgumentNullException();

			EntityInfo entityInfo = null;

#if USE_CACHE_LASTENTITY
			if (_lastEntityInfo != null && _lastEntityInfo.Type == entityType)
				entityInfo = _lastEntityInfo;

			if (entityInfo == null)
				entityInfo = EntityTypeMapping.SafeGet(entityType);
#else
			entityInfo = EntityTypeMapping.SafeGet(entityType);

#endif

			if (entityInfo == null)
				throw new ArgumentOutOfRangeException();

#if USE_CACHE_LASTENTITY
			_lastEntityInfo = entityInfo;
#endif

			var entity =
				mapper != null
					? mapper.CreateEntity(entityInfo.Type)
					: (Entity)Activator.CreateInstance(entityInfo.Type);

			entity.List = entityList;

			entity.OnCreated();

			return entity;
		}

		public static bool FillEntity(Entity entity, IEntityMapper mapper)
		{
			if (entity == null || mapper == null)
				throw new ArgumentNullException();

			entity.IsNew = true;

			entity.Source = null;

			entity.OnLoading();

			var res = mapper.FillEntity(entity);

			entity.OnLoaded();

			return res;
		}

		public static bool LoadEntity(Entity entity, object source, IEntityMapper mapper)
		{
			if (entity == null || source == null || mapper == null)
				throw new ArgumentNullException();

			entity.IsNew = false;

			entity.Source = source;

			entity.OnLoading();

			var res = mapper.LoadEntity(source, entity);

			entity.OnLoaded();

			return res;
		}

		public static bool SaveEntity(object source, Entity entity, IEntityMapper mapper)
		{
			if (entity == null || source == null || mapper == null)
				throw new ArgumentNullException();

			entity.Source = source;

			entity.OnSaving();

			var res = mapper.SaveEntity(entity, source);

			entity.OnSaved();

			return res;
		}
	}
}